#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
天翼TV API封装
参考Go语言版本的189_tv实现
"""

import json
import time
import uuid
import hashlib
import hmac
import base64
import logging
from typing import Dict, Optional, Any, List
from datetime import datetime
import requests
from requests import Session
import sys

# 兼容不同Python版本的int类型
if sys.version_info >= (3, 0):
    int64 = int32 = int
else:
    import __builtin__
    int64 = __builtin__.long
    int32 = __builtin__.int

# 移除对CryptoUtils的依赖，天翼TV不需要复杂的加密功能

# 导入异常类
from ...base.exceptions import CloudStorageError


class RespErr(Exception):
    """响应错误类，参考Go语言实现"""
    
    def __init__(self, res_code=None, res_message="", error="", code="", message="", msg="", error_code="", error_msg=""):
        self.res_code = res_code
        self.res_message = res_message
        self.error = error
        self.code = code
        self.message = message
        self.msg = msg
        self.error_code = error_code
        self.error_msg = error_msg
    
    def has_error(self) -> bool:
        """检查是否有错误"""
        if isinstance(self.res_code, (int, int64, int32)):
            return self.res_code != 0
        elif isinstance(self.res_code, str):
            return self.res_code != ""
        
        return (self.code != "" and self.code != "SUCCESS") or self.error_code != "" or self.error != ""
    
    def __str__(self):
        """错误信息字符串"""
        if isinstance(self.res_code, (int, int64, int32)):
            if self.res_code != 0:
                return f"res_code: {self.res_code}, res_msg: {self.res_message}"
        elif isinstance(self.res_code, str):
            if self.res_code != "":
                return f"res_code: {self.res_code}, res_msg: {self.res_message}"
        
        if self.code != "" and self.code != "SUCCESS":
            if self.msg != "":
                return f"code: {self.code}, msg: {self.msg}"
            elif self.message != "":
                return f"code: {self.code}, msg: {self.message}"
            else:
                return f"code: {self.code}"
        
        if self.error_code != "":
            return f"err_code: {self.error_code}, err_msg: {self.error_msg}"
        
        if self.error != "":
            return f"error: {self.error}, message: {self.message}"
        
        return ""


class TianyiTVApi:
    """天翼TV API封装类"""
    
    # 常量定义，参考Go语言实现
    TV_APP_KEY = "600100885"
    TV_APP_SIGNATURE_SECRET = "fe5734c74c2f96a38157f420b32dc995"
    TV_VERSION = "6.5.5"
    ANDROID_TV = "FAMILY_TV"
    TV_CHANNEL_ID = "home02"
    API_URL = "https://api.cloud.189.cn"
    
    def __init__(self):
        self.session = Session()
        self.logger = logging.getLogger(__name__)
        self.token_info = None
        
        # 设置默认超时时间：连接超时10秒，读取超时60秒
        self.timeout = (10, 60)
        
        # 设置默认请求头
        self.session.headers.update({
            'Accept': 'application/json;charset=UTF-8',
            'User-Agent': 'EcloudTV/6.5.5 (PJX110; unknown; home02) Android/35'
        })
    
    def get_qr_code_uuid(self) -> Dict[str, Any]:
        """
        获取二维码登录UUID
        参考Go语言实现：getQrCodeUUID.action
        """
        try:
            url = f"{self.API_URL}/family/manage/getQrCodeUUID.action"
            
            # 生成签名头
            headers = self._app_key_signature_header(url, 'GET')
            
            response = self.session.get(url, headers=headers, params=self._client_suffix())
            response.raise_for_status()
            
            # 检查响应格式
            if response.headers.get('Content-Type', '').startswith('application/xml'):
                # 解析XML响应
                import xml.etree.ElementTree as ET
                root = ET.fromstring(response.text)
                if root.tag == 'uuidInfo':
                    uuid_element = root.find('uuid')
                    if uuid_element is not None:
                        result = {'uuid': uuid_element.text}
                        self.logger.info(f"获取二维码UUID成功: {result}")
                        return result
                    else:
                        raise Exception("XML响应中没有uuid元素")
                else:
                    raise Exception(f"未知XML格式: {response.text}")
            else:
                # 尝试解析JSON
                result = response.json()
                self.logger.info(f"获取二维码UUID成功: {result}")
                return result
            
        except Exception as e:
            self.logger.error(f"获取二维码UUID失败: {e}")
            raise
    
    def check_qr_code_login_result(self, uuid: str) -> Dict[str, Any]:
        """
        检查二维码登录结果
        严格按照Go语言实现：qrcodeLoginResult.action
        """
        try:
            url = f"{self.API_URL}/family/manage/qrcodeLoginResult.action"
            
            # 生成签名头
            headers = self._app_key_signature_header(url, 'GET')
            
            params = self._client_suffix()
            # 严格按照Go语言实现：直接使用完整的UUID（URL）
            params['uuid'] = uuid
            
            response = self.session.get(url, headers=headers, params=params)
            
            # 打印原始响应信息
            print(f"🔍 API响应状态码: {response.status_code}")
            print(f"📋 API响应头: {dict(response.headers)}")
            print(f"📄 API原始响应内容: {response.text}")
            
            # 严格按照Go语言实现：先检查HTTP状态码
            if response.status_code != 200:
                print(f"❌ HTTP错误: {response.status_code}")
                # 尝试解析错误响应
                try:
                    error_data = response.json()
                    print(f"📊 错误JSON: {error_data}")
                    # 检查是否是QrCodeRollLoginFail（等待状态）
                    if error_data.get('res_code') == 'QrCodeRollLoginFail':
                        print("⏳ 二维码登录等待中...")
                        return None  # 返回None表示还在等待
                    else:
                        print(f"❌ API错误: {error_data}")
                        raise Exception(f"API错误: {error_data}")
                except:
                    # 尝试解析XML错误
                    try:
                        import xml.etree.ElementTree as ET
                        root = ET.fromstring(response.text)
                        print(f"📊 XML错误: {root.tag}")
                        if root.tag == 'error':
                            code = root.find('code')
                            if code is not None and code.text == 'QrCodeRollLoginFail':
                                print("⏳ 二维码登录等待中...")
                                return None  # 返回None表示还在等待
                            else:
                                print(f"❌ XML错误: {response.text}")
                                raise Exception(f"XML错误: {response.text}")
                        else:
                            print(f"❌ 未知XML格式: {response.text}")
                            raise Exception(f"未知XML格式: {response.text}")
                    except:
                        response.raise_for_status()
            
            # HTTP 200状态码，尝试解析响应
            try:
                result = response.json()
                print(f"📊 成功JSON响应: {result}")
                
                # 检查是否包含错误信息
                if 'res_code' in result and result['res_code'] == 'QrCodeRollLoginFail':
                    print("⏳ 二维码登录等待中...")
                    return None  # 返回None表示还在等待
                
                # 检查是否包含accessToken（成功登录）
                if 'accessToken' in result and result['accessToken']:
                    print(f"✅ 登录成功，获取到accessToken: {result['accessToken']}")
                    return result
                
                # 其他情况，返回原始结果
                print(f"📋 其他响应: {result}")
                return result
                
            except Exception as json_error:
                print(f"❌ JSON解析失败: {json_error}")
                # 尝试解析XML
                try:
                    import xml.etree.ElementTree as ET
                    root = ET.fromstring(response.text)
                    print(f"📊 XML响应: {root.tag}")
                    
                    if root.tag == 'error':
                        code = root.find('code')
                        if code is not None and code.text == 'QrCodeRollLoginFail':
                            print("⏳ 二维码登录等待中...")
                            return None  # 返回None表示还在等待
                        else:
                            print(f"❌ XML错误: {response.text}")
                            raise Exception(f"XML错误: {response.text}")
                    else:
                        # 可能是成功的XML响应
                        print(f"📋 XML响应内容: {response.text}")
                        return response.text
                        
                except Exception as xml_error:
                    print(f"❌ XML解析失败: {xml_error}")
                    # 返回原始文本
                    print(f"📋 原始响应: {response.text}")
                    return response.text
            
        except Exception as e:
            print(f"❌ 检查二维码登录结果失败: {e}")
            self.logger.error(f"检查二维码登录结果失败: {e}")
            raise
    
    def login_family_merge(self, e189_access_token: str) -> Dict[str, Any]:
        """
        使用E189AccessToken登录获取SessionKey和SessionSecret
        参考Go语言实现：loginFamilyMerge.action
        """
        try:
            url = f"{self.API_URL}/family/manage/loginFamilyMerge.action"
            
            # 生成签名头
            headers = self._app_key_signature_header(url, 'GET')
            
            params = self._client_suffix()
            params['e189AccessToken'] = e189_access_token
            
            response = self.session.get(url, headers=headers, params=params)
            response.raise_for_status()
            
            result = response.json()
            self.logger.info(f"登录合并成功: {result}")
            return result
            
        except Exception as e:
            self.logger.error(f"登录合并失败: {e}")
            raise
    
    def get_user_info(self, access_token: str) -> Dict[str, Any]:
        """
        获取用户信息
        参考Go语言实现：getUserInfo.action
        """
        try:
            url = f"{self.API_URL}/getUserInfo.action"
            
            # 生成签名头
            headers = self._signature_header(url, 'GET', False)
            
            response = self.session.get(url, headers=headers, params=self._client_suffix())
            response.raise_for_status()
            
            result = response.json()
            self.logger.info(f"获取用户信息成功: {result}")
            return result
            
        except Exception as e:
            self.logger.error(f"获取用户信息失败: {e}")
            raise
    
    def get_files(self, folder_id: str, access_token: str, is_family: bool = False) -> List[Dict[str, Any]]:
        """
        获取文件列表
        参考Go语言实现：listFiles.action
        """
        try:
            url = f"{self.API_URL}/listFiles.action"
            if is_family:
                url = f"{self.API_URL}/family/file/listFiles.action"
            
            # 生成签名头
            headers = self._signature_header(url, 'GET', is_family)
            
            params = self._client_suffix()
            params.update({
                'folderId': folder_id,
                'fileType': '0',
                'mediaAttr': '0',
                'iconOption': '5',
                'pageNum': '1',
                'pageSize': '130'
            })
            
            if is_family:
                params.update({
                    'familyId': self.token_info.get('familyId', ''),
                    'orderBy': 'createDate',
                    'descending': 'true'
                })
            else:
                params.update({
                    'recursive': '0',
                    'orderBy': 'createDate',
                    'descending': 'true'
                })
            
            response = self.session.get(url, headers=headers, params=params)
            response.raise_for_status()
            
            result = response.json()
            files = []
            
            # 处理文件夹
            if 'fileListAO' in result and 'folderList' in result['fileListAO']:
                for folder in result['fileListAO']['folderList']:
                    folder['isFolder'] = True
                    files.append(folder)
            
            # 处理文件
            if 'fileListAO' in result and 'fileList' in result['fileListAO']:
                for file in result['fileListAO']['fileList']:
                    file['isFolder'] = False
                    files.append(file)
            
            self.logger.info(f"获取文件列表成功，共{len(files)}个文件/文件夹")
            return files
            
        except Exception as e:
            self.logger.error(f"获取文件列表失败: {e}")
            raise
    
    def create_folder(self, name: str, parent_id: str, access_token: str, is_family: bool = False) -> Dict[str, Any]:
        """
        创建文件夹
        参考Go语言实现：createFolder.action
        """
        try:
            url = f"{self.API_URL}/createFolder.action"
            if is_family:
                url = f"{self.API_URL}/family/file/createFolder.action"
            
            # 生成签名头
            headers = self._signature_header(url, 'POST', is_family)
            
            params = self._client_suffix()
            params.update({
                'folderName': name,
                'relativePath': ''
            })
            
            if is_family:
                params.update({
                    'familyId': self.token_info.get('familyId', ''),
                    'parentId': parent_id
                })
            else:
                params['parentFolderId'] = parent_id
            
            response = self.session.post(url, headers=headers, params=params)
            response.raise_for_status()
            
            result = response.json()
            self.logger.info(f"创建文件夹成功: {result}")
            return result
            
        except Exception as e:
            self.logger.error(f"创建文件夹失败: {e}")
            raise
    
    def get_download_url(self, file_id: str, access_token: str, is_family: bool = False) -> str:
        """
        获取文件下载链接
        参考Go语言实现：getFileDownloadUrl.action
        """
        try:
            url = f"{self.API_URL}/getFileDownloadUrl.action"
            if is_family:
                url = f"{self.API_URL}/family/file/getFileDownloadUrl.action"
            
            # 生成签名头
            headers = self._signature_header(url, 'GET', is_family)
            
            params = self._client_suffix()
            params['fileId'] = file_id
            
            if is_family:
                params['familyId'] = self.token_info.get('familyId', '')
            else:
                params.update({
                    'dt': '3',
                    'flag': '1'
                })
            
            # 添加超时设置，增加连接和读取超时
            response = self.session.get(url, headers=headers, params=params, timeout=(10, 15))
            response.raise_for_status()
            
            result = response.json()
            self.logger.info(f"获取下载链接API响应: {result}")
            download_url = result.get('fileDownloadUrl', '')
            
            # 处理重定向获取真实链接
            if download_url:
                download_url = download_url.replace('&amp;', '&').replace('http://', 'https://', 1)
                
                try:
                    # 检查重定向，设置超时
                    redirect_response = requests.get(download_url, allow_redirects=False, timeout=(5, 10))
                    if redirect_response.status_code == 302:
                        new_url = redirect_response.headers.get('location', download_url)
                        if new_url and new_url != download_url:
                            self.logger.info(f"检测到重定向，更新下载链接")
                            download_url = new_url
                except Exception as e:
                    self.logger.warning(f"处理重定向时出错，使用原始链接: {e}")
            else:
                self.logger.warning(f"API返回的下载链接为空，响应内容: {result}")
                raise Exception("获取下载链接失败：返回的链接为空")
            
            self.logger.info(f"获取下载链接成功: {download_url}")
            return download_url
            
        except Exception as e:
            self.logger.error(f"获取下载链接失败: {e}")
            raise
    
    def delete_file(self, file_id: str, access_token: str, is_family: bool = False) -> Dict[str, Any]:
        """
        删除文件
        参考Go语言实现：批量删除任务
        """
        try:
            # 创建批量删除任务
            url = f"{self.API_URL}/batch/createBatchTask.action"
            
            # 生成签名头
            headers = self._signature_header(url, 'POST', is_family)
            
            params = self._client_suffix()
            task_infos = [{
                'fileId': file_id,
                'fileName': 'unknown',  # 这里需要从文件信息中获取
                'isFolder': 0  # 假设是文件，文件夹为1
            }]
            
            data = {
                'type': 'DELETE',
                'taskInfos': json.dumps(task_infos)
            }
            
            if is_family:
                data['familyId'] = self.token_info.get('familyId', '')
            
            response = self.session.post(url, headers=headers, params=params, data=data)
            response.raise_for_status()
            
            result = response.json()
            task_id = result.get('taskId')
            
            if task_id:
                # 等待任务完成
                self._wait_batch_task('DELETE', task_id)
            
            self.logger.info(f"删除文件成功: {result}")
            return result
            
        except Exception as e:
            self.logger.error(f"删除文件失败: {e}")
            raise
    
    def rename_file(self, file_id: str, new_name: str, access_token: str, is_family: bool = False) -> Dict[str, Any]:
        """
        重命名文件
        参考Go语言实现：renameFile.action / renameFolder.action
        """
        try:
            # 这里需要根据文件类型选择不同的API
            # 简化实现，假设是文件
            url = f"{self.API_URL}/renameFile.action"
            if is_family:
                url = f"{self.API_URL}/family/file/renameFile.action"
            
            # 生成签名头
            headers = self._signature_header(url, 'GET', is_family)
            
            params = self._client_suffix()
            params.update({
                'fileId': file_id,
                'destFileName': new_name
            })
            
            if is_family:
                params['familyId'] = self.token_info.get('familyId', '')
            
            response = self.session.get(url, headers=headers, params=params)
            response.raise_for_status()
            
            result = response.json()
            self.logger.info(f"重命名文件成功: {result}")
            return result
            
        except Exception as e:
            self.logger.error(f"重命名文件失败: {e}")
            raise

    
    def move_file(self, file_id: str, target_folder_id: str, access_token: str, is_family: bool = False, file_name: str = None, is_folder: bool = False) -> Dict[str, Any]:
        """
        移动文件
        参考TV版API参数格式：type=MOVE&taskInfos=%5B%7B%22fileId%22%3A%22424891220150169064%22%2C%22fileName%22%3A%22%E5%85%A8%E5%B1%8F%E5%87%80%E6%B0%B4%E6%96%B9%E6%A1%88.xlsx%22%2C%22isFolder%22%3A0%7D%5D&targetFolderId=124991219021700641
        """
        try:
            # 创建批量任务
            url = f"{self.API_URL}/batch/createBatchTask.action"
            if is_family:
                url = f"{self.API_URL}/family/batch/createBatchTask.action"
            
            # 生成签名头
            headers = self._signature_header(url, 'POST', is_family)
            
            # 构建任务信息，与TV版API参数格式保持一致
            # TV版API参数格式：type=MOVE&taskInfos=[{"fileId":"xxx","fileName":"xxx","isFolder":0}]&targetFolderId=xxx
            task_info = {
                'type': 'MOVE',
                'taskInfos': json.dumps([{
                    'fileId': file_id,
                    'fileName': file_name or '',  # 文件名，如果未提供则使用空字符串
                    'isFolder': 1 if is_folder else 0  # 注意：这里是数字0/1，不是字符串"0"/"1"
                }]),
                'targetFolderId': target_folder_id  # 目标文件夹ID
            }
            
            # 添加家庭ID（如果是家庭模式）
            if is_family:
                task_info['familyId'] = self.token_info.get('familyId', '')
            
            # 添加更详细的日志记录
            self.logger.info(f"创建移动文件任务: fileId={file_id}, fileName={file_name}, targetFolderId={target_folder_id}")
            self.logger.debug(f"任务信息: {task_info}")
            
            response = self.session.post(url, headers=headers, data=task_info)
            response.raise_for_status()
            
            result = response.json()
            res_code = result.get('res_code', 1)
            
            if res_code != 0:
                error_msg = result.get('res_message', '未知错误')
                self.logger.error(f"创建移动文件任务失败: {error_msg}")
                raise CloudStorageError(f"移动文件失败: {error_msg}")
            
            task_id = result.get('taskId', '')
            if not task_id:
                self.logger.error("移动文件任务ID为空")
                raise CloudStorageError("移动文件失败: 任务ID为空")
            
            self.logger.info(f"移动文件任务创建成功: {task_id}")
            
            # 等待任务完成，与Go实现保持一致，使用400毫秒间隔
            if self._wait_batch_task('MOVE', task_id):
                self.logger.info(f"移动文件任务完成: {task_id}")
                return {
                    'res_code': 0,
                    'res_message': '移动成功',
                    'taskId': task_id
                }
            else:
                self.logger.error(f"移动文件任务失败或超时: {task_id}")
                raise CloudStorageError("移动文件失败: 任务执行失败或超时")
            
        except Exception as e:
            self.logger.error(f"移动文件异常: {e}")
            raise
    
    def search_files(self, query: str, access_token: str, is_family: bool = False) -> List[Dict[str, Any]]:
        """
        搜索文件
        参考Go语言实现：searchFiles.action
        """
        try:
            url = f"{self.API_URL}/searchFiles.action"
            if is_family:
                url = f"{self.API_URL}/family/file/searchFiles.action"
            
            # 生成签名头
            headers = self._signature_header(url, 'GET', is_family)
            
            params = self._client_suffix()
            params.update({
                'keyword': query,
                'pageNum': '1',
                'pageSize': '100'
            })
            
            if is_family:
                params['familyId'] = self.token_info.get('familyId', '')
            
            response = self.session.get(url, headers=headers, params=params)
            response.raise_for_status()
            
            result = response.json()
            files = []
            
            # 处理搜索结果
            if 'fileListAO' in result:
                if 'folderList' in result['fileListAO']:
                    for folder in result['fileListAO']['folderList']:
                        folder['isFolder'] = True
                        files.append(folder)
                
                if 'fileList' in result['fileListAO']:
                    for file in result['fileListAO']['fileList']:
                        file['isFolder'] = False
                        files.append(file)
            
            self.logger.info(f"搜索文件成功，共{len(files)}个结果")
            return files
            
        except Exception as e:
            self.logger.error(f"搜索文件失败: {e}")
            raise
    
    def get_capacity_info(self, access_token: str) -> Dict[str, Any]:
        """
        获取容量信息
        参考Go语言实现：getUserSizeInfo.action
        """
        try:
            url = f"{self.API_URL}/portal/getUserSizeInfo.action"
            
            # 生成签名头
            headers = self._signature_header(url, 'GET', False)
            
            response = self.session.get(url, headers=headers, params=self._client_suffix())
            response.raise_for_status()
            
            result = response.json()
            self.logger.info(f"获取容量信息成功: {result}")
            return result
            
        except Exception as e:
            self.logger.error(f"获取容量信息失败: {e}")
            raise
    
    def keep_alive(self, access_token: str) -> bool:
        """
        保持会话活跃
        参考Go语言实现：keepAlive
        """
        try:
            url = f"{self.API_URL}/keepUserSession.action"
            
            # 生成签名头
            headers = self._signature_header(url, 'GET', False)
            
            response = self.session.get(url, headers=headers, params=self._client_suffix())
            response.raise_for_status()
            
            self.logger.debug("保持会话活跃成功")
            return True
            
        except Exception as e:
            self.logger.warning(f"保持会话活跃失败: {e}")
            # 如果keepAlive失败，尝试刷新session
            if not self.refresh_session():
                self.logger.error(f"会话刷新失败: {e}")
            return False
    
    def _client_suffix(self) -> Dict[str, str]:
        """获取客户端后缀参数"""
        return {
            'clientType': self.ANDROID_TV,  # "FAMILY_TV"
            'version': self.TV_VERSION,
            'channelId': self.TV_CHANNEL_ID,
            'clientSn': 'unknown',
            'model': 'PJX110',
            'osFamily': 'Android',
            'osVersion': '35',
            'networkAccessMode': 'WIFI',
            'telecomsOperator': '46011'
        }
    
    def _app_key_signature_header(self, url: str, method: str) -> Dict[str, str]:
        """生成AppKey签名头"""
        # 使用毫秒时间戳，与Go语言实现一致
        timestamp = int(time.time() * 1000)
        
        # 生成签名
        signature = self._app_key_signature_of_hmac(
            self.TV_APP_SIGNATURE_SECRET,
            self.TV_APP_KEY,
            method,
            url,
            timestamp
        )
        
        return {
            'Timestamp': str(timestamp),
            'X-Request-ID': str(uuid.uuid4()),
            'AppKey': self.TV_APP_KEY,
            'AppSignature': signature
        }
    
    def _signature_header(self, url: str, method: str, is_family: bool) -> Dict[str, str]:
        """生成SessionKey签名头"""
        if not self.token_info:
            raise ValueError("Token信息未设置")
        
        date_str = self._get_http_date_str()
        session_key = self.token_info.get('sessionKey', '')
        session_secret = self.token_info.get('sessionSecret', '')
        
        if is_family:
            session_key = self.token_info.get('familySessionKey', '')
            session_secret = self.token_info.get('familySessionSecret', '')
        
        # 生成签名
        signature = self._session_key_signature_of_hmac(
            session_secret,
            session_key,
            method,
            url,
            date_str
        )
        
        return {
            'Date': date_str,
            'SessionKey': session_key,
            'X-Request-ID': str(uuid.uuid4()),
            'Signature': signature
        }
    
    def _app_key_signature_of_hmac(self, secret: str, app_key: str, method: str, url: str, timestamp: int) -> str:
        """生成AppKey HMAC签名"""
        import re
        
        # 提取URL路径，与Go语言实现一致
        urlpath_match = re.search(r'://[^/]+((/[^/\s?#]+)*)', url)
        if urlpath_match:
            urlpath = urlpath_match.group(1)
        else:
            urlpath = url
        
        # 构建签名字符串，与Go语言实现一致
        sign_string = f"AppKey={app_key}&Operate={method}&RequestURI={urlpath}&Timestamp={timestamp}"
        
        # 生成HMAC签名
        signature = hmac.new(
            secret.encode('utf-8'),
            sign_string.encode('utf-8'),
            hashlib.sha1
        ).digest()
        
        return signature.hex().upper()
    
    def create_upload_file(self, parent_id: str, file_md5: str, file_name: str, file_size: str, is_family: bool = False) -> Dict[str, Any]:
        """
        创建上传文件会话 - 支持秒传检测
        参考Go语言实现：OldUploadCreate
        """
        try:
            if is_family:
                url = f"{self.API_URL}/family/file/createFamilyFile.action"
                params = {
                    'familyId': self.token_info.get('familyId', ''),
                    'parentId': parent_id,
                    'fileMd5': file_md5,
                    'fileName': file_name,
                    'fileSize': file_size,
                    'resumePolicy': '1'
                }
            else:
                url = f"{self.API_URL}/createUploadFile.action"
                params = {
                    'parentFolderId': parent_id,
                    'fileName': file_name,
                    'size': file_size,
                    'md5': file_md5,
                    'opertype': '3',
                    'flag': '1',
                    'resumePolicy': '1',
                    'isLog': '0'
                }
            
            # 生成签名头
            headers = self._signature_header(url, 'POST', is_family)
            
            if is_family:
                response = self.session.post(url, headers=headers, params=params, timeout=self.timeout)
            else:
                response = self.session.post(url, headers=headers, data=params, timeout=self.timeout)
            
            response.raise_for_status()
            result = response.json()
            
            self.logger.info(f"创建上传文件会话成功: {result}")
            return result
            
        except Exception as e:
            self.logger.error(f"创建上传文件会话失败: {e}")
            raise
    
    def commit_upload_file(self, commit_url: str, upload_file_id: int, is_family: bool = False, overwrite: bool = True) -> Dict[str, Any]:
        """
        提交上传文件
        参考Go语言实现：OldUploadCommit
        """
        try:
            headers = self._signature_header(commit_url, 'POST', is_family)
            
            if is_family:
                # 家庭空间使用Headers
                headers.update({
                    'ResumePolicy': '1',
                    'UploadFileId': str(upload_file_id),
                    'FamilyId': str(self.token_info.get('familyId', ''))
                })
                response = self.session.post(commit_url, headers=headers, timeout=self.timeout)
            else:
                # 个人空间使用FormData
                headers.update({
                    'ResumePolicy': '1'
                })
                data = {
                    'opertype': '3' if overwrite else '1',
                    'resumePolicy': '1',
                    'uploadFileId': str(upload_file_id),
                    'isLog': '0'
                }
                response = self.session.post(commit_url, headers=headers, data=data, timeout=self.timeout)
            
            response.raise_for_status()
            
            # 打印响应信息用于调试
            print(f"🔍 提交上传响应状态码: {response.status_code}")
            print(f"🔍 提交上传响应头: {dict(response.headers)}")
            print(f"🔍 提交上传响应内容: {response.text}")
            
            # 检查响应内容类型
            content_type = response.headers.get('content-type', '').lower()
            
            # 如果是XML格式响应
            if 'application/xml' in content_type or 'text/xml' in content_type or response.text.strip().startswith('<?xml') or '<file>' in response.text:
                try:
                    import xml.etree.ElementTree as ET
                    root = ET.fromstring(response.text)
                    if root.tag == 'file':
                        file_data = {}
                        for child in root:
                            file_data[child.tag] = child.text
                        result = {"file": file_data}
                        self.logger.info(f"提交上传文件成功（XML）: {result}")
                        return result
                    else:
                        return {"raw_response": response.text}
                except Exception as xml_error:
                    print(f"❌ XML解析失败: {xml_error}")
                    return {"raw_response": response.text}
            else:
                # 尝试JSON解析
                try:
                    result = response.json()
                    self.logger.info(f"提交上传文件成功: {result}")
                    return result
                except Exception as json_error:
                    print(f"❌ JSON解析失败: {json_error}")
                    # 如果JSON解析失败，返回原始响应
                    return {"raw_response": response.text}
            
        except Exception as e:
            self.logger.error(f"提交上传文件失败: {e}")
            raise
    
    def get_upload_file_status(self, upload_file_id: int, is_family: bool = False) -> Dict[str, Any]:
        """
        获取上传文件状态
        参考Go语言实现：getUploadFileStatus
        """
        try:
            if is_family:
                url = f"{self.API_URL}/family/file/getFamilyFileStatus.action"
                params = {
                    'uploadFileId': str(upload_file_id),
                    'resumePolicy': '1',
                    'familyId': str(self.token_info.get('familyId', ''))
                }
            else:
                url = f"{self.API_URL}/getUploadFileStatus.action"
                params = {
                    'uploadFileId': str(upload_file_id),
                    'resumePolicy': '1'
                }
            
            headers = self._signature_header(url, 'GET', is_family)
            response = self.session.get(url, headers=headers, params=params)
            response.raise_for_status()
            result = response.json()
            
            self.logger.info(f"获取上传文件状态成功: {result}")
            return result
            
        except Exception as e:
            self.logger.error(f"获取上传文件状态失败: {e}")
            raise

    def upload_file_data(self, upload_url: str, file_data: bytes, upload_file_id: int, is_family: bool = False) -> bool:
        """
        上传文件数据
        参考Go语言实现：OldUpload中的文件上传部分
        """
        try:
            # 生成签名头
            headers = self._signature_header(upload_url, 'PUT', is_family)
            
            # 添加上传特定的头
            headers.update({
                'ResumePolicy': '1',
                'Expect': '100-continue'
            })
            
            if is_family:
                headers['FamilyId'] = str(self.token_info.get('familyId', ''))
                headers['UploadFileId'] = str(upload_file_id)
            else:
                headers['Edrive-UploadFileId'] = str(upload_file_id)
            
            # 上传文件数据，添加超时设置：连接超时10秒，读取超时300秒（5分钟）
            response = self.session.put(upload_url, headers=headers, data=file_data, timeout=(10, 300))
            response.raise_for_status()
            
            self.logger.info(f"文件数据上传成功，大小: {len(file_data)} 字节")
            return True
            
        except Exception as e:
            self.logger.error(f"文件数据上传失败: {e}")
            raise

    def _session_key_signature_of_hmac(self, secret: str, session_key: str, method: str, url: str, date: str) -> str:
        """生成SessionKey HMAC签名"""
        import re
        
        # 提取URL路径，与Go语言实现一致
        urlpath_match = re.search(r'://[^/]+((/[^/\s?#]+)*)', url)
        if urlpath_match:
            urlpath = urlpath_match.group(1)
        else:
            urlpath = url
        
        # 构建签名字符串，与Go语言实现一致
        sign_string = f"SessionKey={session_key}&Operate={method}&RequestURI={urlpath}&Date={date}"
        
        # 生成HMAC签名
        signature = hmac.new(
            secret.encode('utf-8'),
            sign_string.encode('utf-8'),
            hashlib.sha1
        ).digest()
        
        return signature.hex().upper()
    
    def _get_http_date_str(self) -> str:
        """获取HTTP日期字符串"""
        return datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT')
    
    def refresh_session(self) -> bool:
        """
        刷新会话
        参考Go语言实现：refreshSession
        """
        try:
            if not self.token_info or not self.token_info.get('accessToken'):
                raise ValueError("AccessToken未设置")
            
            url = f"{self.API_URL}/family/manage/loginFamilyMerge.action"
            
            # 生成签名头
            headers = self._app_key_signature_header(url, 'GET')
            
            params = self._client_suffix()
            params['e189AccessToken'] = self.token_info.get('accessToken')
            
            response = self.session.get(url, headers=headers, params=params)
            response.raise_for_status()
            
            result = response.json()
            
            # 更新token信息
            self.token_info.update(result)
            
            self.logger.info("会话刷新成功")
            return True
            
        except Exception as e:
            self.logger.error(f"会话刷新失败: {e}")
            return False
    
    def _wait_batch_task(self, task_type: str, task_id: str, max_wait_time: int = 60) -> bool:
        """等待批量任务完成，优化为与Go语言实现一致"""
        start_time = time.time()
        check_count = 0
        
        self.logger.info(f"开始等待批量任务完成: taskType={task_type}, taskId={task_id}, 最大等待时间={max_wait_time}秒")
        
        while time.time() - start_time < max_wait_time:
            try:
                check_count += 1
                # 检查任务状态
                url = f"{self.API_URL}/batch/checkBatchTask.action"
                headers = self._signature_header(url, 'POST', False)
                
                data = {
                    'type': task_type,
                    'taskId': task_id
                }
                
                response = self.session.post(url, headers=headers, data=data)
                response.raise_for_status()
                
                result = response.json()
                task_status = result.get('taskStatus', 0)
                
                # 减少日志记录频率，只在关键状态变化时记录
                if task_status in [2, 3, 4]:  # 只在状态变化时记录
                    self.logger.info(f"批量任务状态检查: taskType={task_type}, taskId={task_id}, taskStatus={task_status}, 检查次数={check_count}")
                
                if task_status == 4:  # 完成
                    self.logger.info(f"批量任务完成: {task_id}, 总检查次数: {check_count}")
                    # 增加额外等待时间，确保文件同步完成
                    time.sleep(2)
                    return True
                elif task_status == 2:  # 冲突
                    self.logger.warning(f"批量任务冲突: {task_id}")
                    return False
                elif task_status == 3:  # 失败
                    self.logger.error(f"批量任务失败: {task_id}")
                    return False
                
                # 使用固定等待时间，与Go语言实现保持一致
                time.sleep(1.0)
                
            except Exception as e:
                self.logger.error(f"检查批量任务状态失败: {e}")
                time.sleep(1.0)  # 异常时使用更长的等待时间
        
        self.logger.warning(f"批量任务超时: {task_id}, 总检查次数: {check_count}")
        return False

    def get_file_info(self, file_id: str, access_token: str, folder_id: str, is_family: bool = False) -> Dict[str, Any]:
        """
        获取文件信息
        优化：在指定文件夹ID下查看文件信息，查询逻辑是扫描该文件夹下的文件列表，查找指定文件ID的文件
        """
        try:
            # 直接在指定文件夹中查找文件
            return self.get_file_info_in_folder(file_id, folder_id, access_token, is_family)
            
        except Exception as e:
            self.logger.error(f"获取文件信息失败: {e}")
            raise

    def list_files_in_folder(self, folder_id: str, access_token: str, is_family: bool = False) -> List[Dict[str, Any]]:
        """
        列出指定文件夹中的文件
        参考Go语言实现：listFiles.action
        """
        try:
            url = f"{self.API_URL}/listFiles.action"
            if is_family:
                url = f"{self.API_URL}/family/file/listFiles.action"
            
            # 生成签名头
            headers = self._signature_header(url, 'GET', is_family)
            
            params = self._client_suffix()
            params.update({
                'folderId': folder_id,
                'fileType': '0',
                'mediaAttr': '0',
                'iconOption': '5',
                'pageNum': '1',
                'pageSize': '130'
            })
            
            if is_family:
                params.update({
                    'familyId': self.token_info.get('familyId', ''),
                    'orderBy': 'createDate',
                    'descending': 'true'
                })
            else:
                params.update({
                    'recursive': '0',
                    'orderBy': 'createDate',
                    'descending': 'true'
                })
            
            response = self.session.get(url, headers=headers, params=params)
            response.raise_for_status()
            
            result = response.json()
            files = []
            
            # 处理文件夹
            if 'fileListAO' in result and 'folderList' in result['fileListAO']:
                for folder in result['fileListAO']['folderList']:
                    folder['isFolder'] = True
                    files.append(folder)
            
            # 处理文件
            if 'fileListAO' in result and 'fileList' in result['fileListAO']:
                for file in result['fileListAO']['fileList']:
                    file['isFolder'] = False
                    files.append(file)
            
            self.logger.info(f"获取文件夹 {folder_id} 中的文件列表成功，共{len(files)}个文件/文件夹")
            return files
            
        except Exception as e:
            self.logger.error(f"获取文件夹中的文件列表失败: {e}")
            raise

    def get_file_info_in_folder(self, file_id: str, folder_id: str, access_token: str, is_family: bool = False) -> Optional[Dict[str, Any]]:
        """
        在指定文件夹中获取文件信息
        """
        try:
            # 先列出文件夹中的所有文件
            files = self.list_files_in_folder(folder_id, access_token, is_family)
            
            # 查找目标文件
            for file in files:
                if str(file.get('id', '')) == file_id:
                    # 确保返回的文件信息中包含正确的parentId
                    # 如果API返回的parentId为空，则使用当前文件夹ID作为parentId
                    if not file.get('parentId'):
                        file['parentId'] = folder_id
                    return file
            
            return None
            
        except Exception as e:
            self.logger.error(f"在文件夹 {folder_id} 中查找文件 {file_id} 失败: {e}")
            raise
